// ===================================
// <copyright>
// This file is part of LABSMFController.
//
//    LABSMFController is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    LABSMFController is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with LABSMFController.  If not, see <http://www.gnu.org/licenses/>.
//    Copyright 2011 David Bakker
//
//</copyright>
// <author>David Bakker</author>
// <email>mailto:DavidFBakker@gmail.com</email>
// <created>Saturday, May 07, 2011</created>
// <lastedit>Saturday, May 07, 2011</lastedit>
// ===================================

using System;
using System.Threading;
using GHIElectronics.NETMF.FEZ;
using LABSMFController.LABS;
using LABSMFController.LABS.Classses;
using LABSMFController.LABS.Devices;

namespace LABSMFController.LABS.Devices
{
    public static class VValveDevices
    {
      
        public static int MAXVValves { get { return 7; }
            
        }

        static VValveDevices()
        {
            
        }

        private static object _locker;
        private static object Locker
        {
            get
            {
                if (_locker==null)
                    _locker=new object();

                return _locker;
            }
            set { _locker = value; }
        }

        private static VariableValveDevice[] _vValves;
        public static VariableValveDevice[] VValves
        {
            get
            {
                lock (Locker)
                {
                    if (_vValves == null)

                        SetupVValves();
                
                return _vValves;
                }
            }
            set
            {
                _vValves = value;
            }
        }

        public static void ResetAllVVavles()
        {
           
            for (int c = 0; c < MAXVValves; c++)
            {
                if (VValves[c].IsThreeWay)
                {
                    if (VValves[c].DefaultAorB.Equals("A"))
                    {
                        VValves[c].TurnToA();
                    }
                    else
                    {
                        VValves[c].TurnToB();
                    }
                }
                else
                {
                    VValves[c].TurnOn();
                }
            }
        }



        private static void SetupVValves()
        {
            
            Location.Setup();
            VValves = new VariableValveDevice[MAXVValves];

            VValves[VVariableENUM.Pump0_VValve] = new VariableValveDevice( "HLT VValve " + 0, Devs.RelayDevice.Pump1_VValve.LoadPIN, Devs.RelayDevice.Pump1_VValve.ControlPIN, false, "");
            VValves[VVariableENUM.Pump1_VValve] = new VariableValveDevice("BK VValve " + 1, Devs.RelayDevice.Pump0_VValve.LoadPIN, Devs.RelayDevice.Pump0_VValve.ControlPIN, false, "");

            VValves[VVariableENUM.ThreeWay_3_1] = new VariableValveDevice("Three Way 3-1", Devs.RelayDevice.ThreeWay_3_1.LoadPIN, Devs.RelayDevice.ThreeWay_3_1.ControlPIN, true, "A");
            VValves[VVariableENUM.ThreeWay_3_2] = new VariableValveDevice("Three Way 3-2", Devs.RelayDevice.ThreeWay_3_2.LoadPIN, Devs.RelayDevice.ThreeWay_3_2.ControlPIN, true, "B");
            VValves[VVariableENUM.ThreeWay_3_3] = new VariableValveDevice("Three Way 3-3", Devs.RelayDevice.ThreeWay_3_3.LoadPIN, Devs.RelayDevice.ThreeWay_3_3.ControlPIN, true, "B");
            VValves[VVariableENUM.ThreeWay_3_4] = new VariableValveDevice("Three Way 3-4", Devs.RelayDevice.ThreeWay_3_4.LoadPIN, Devs.RelayDevice.ThreeWay_3_4.ControlPIN, true, "B");

            VValves[VVariableENUM.HLT_KettleValve] = new VariableValveDevice("HLT Kettle VValve ", Devs.RelayDevice.HLT_KettleValve.LoadPIN, Devs.RelayDevice.HLT_KettleValve.ControlPIN, false, "");
        }


        public static string GetVValvesClientOutPut()
        {
            string ret = "HEADER:VValves:" + VValves[0].Header + ";";
            for (int c = 0; c < MAXVValves; c++)
            {
                ret = ret + "\r\n" + VValves[c].GetClientOutPut();
            }
            return ret;
        }

        #region Nested type: VariableValveDevice
    }
        public class VariableValveDevice
        {
            public VariableValveDevice(string description,
                                       FEZ_Components.Relay16.RelayChannel relayChannelLoad,
                                       FEZ_Components.Relay16.RelayChannel relayChannelControl, bool isThreeWay,
                                       string defaultAorB)
            {
                
                Description = description;
                RelayLoad = relayChannelLoad;
                RelayControl = relayChannelControl;

                RelayLoadNum = Convert.ToInt32(RelayLoad.ToString());
                RelayControlNum = Convert.ToInt32(RelayControl.ToString());
                IsRunning = false;
                Swing = 0;
                FullSwingSeconds = Convert.ToInt32(Settings.GetSetting("FullSwingSeconds"));
                Header =
                    "ValveNum,Description,IsThreeWay,AorB,ASwing,BSwing,DefaultAorB,FullSwingSeconds,Swing,IsRunning";
                IsThreeWay = isThreeWay;
                DefaultAorB = defaultAorB;
                AorB = "";
                FirstRun = true;
                //if (IsThreeWay)
                //    FullSwingSeconds = 95;
                ASwing = 100;
                BSwing = 0;
            }

            public string Description { get; private set; }
            internal FEZ_Components.Relay16.RelayChannel RelayLoad { get; set; }
            internal FEZ_Components.Relay16.RelayChannel RelayControl { get; set; }
            public bool IsOn { get; private set; }
            public int RelayLoadNum { get; private set; }
            public int RelayControlNum { get; private set; }
            public int FullSwingSeconds { get; internal set; }
            internal int VValveNum { get; private set; }
            public double Swing { get; set; }
            public int ASwing { get; set; }
            public int BSwing { get; set; }
            private bool FirstRun { get; set; }
            public string AorB { get; set; }
            public string DefaultAorB { get; set; }
            public bool IsThreeWay { get; set; }
            public bool IsRunning { get; set; }

            internal string Header { get; set; }

            public string GetClientOutPut()
            {
                string ret = VValveNum + "," + Description + "," + IsThreeWay + "," + AorB + "," + ASwing + "," + BSwing +
                             "," + DefaultAorB + "," + FullSwingSeconds + "," + Swing + "," + IsRunning + ";";
                return ret;
            }

            public void TurnLevel(int level, bool ignoreSwing)
            {
                try
                {
                    while (IsRunning)
                        Thread.Sleep(100);


                    IsRunning = true;
                    BeerProgram.Log("Turning VValve " + Description + " to " + level, LoggingSeverity.Debug);

                    if (level == Swing && ! ignoreSwing)
                    {
                        IsRunning = false;
                        return;
                    }

                    var op = new OperateVValveThread();
                    op.DoRun(this, level, ignoreSwing);
                }
                catch
                {
                    EventHandlers.InvokePanicTriggerEvent("Cannot turn on valve " + Description);
                }
                IsOn = true;
            }

            public void TurnOn()
            {
                TurnLevel(100, true);

                IsOn = true;
            }

            public void TurnToA()
            {
                if (FirstRun)
                    Swing = 0;

                TurnLevel(ASwing, FirstRun);

                AorB = "A";

                if (FirstRun)
                {
                    FirstRun = false;
                }
            }

            public void TurnToB()
            {
                if (FirstRun)
                    Swing = 100;
                TurnLevel(BSwing, FirstRun);

                AorB = "B";

                if (FirstRun)
                {
                    FirstRun = false;
                }
            }
        }

        #endregion
    }

    internal class OperateVValveThread
    {
        public VariableValveDevice VariableValveDevice;
        private int _count;
        private int _delayMilli;
        private double _newvalue;
        private int _si;
        private double _step;
        public int Level { get; set; }
        private bool IgnoreSwing { get; set; }

        public void DoRun(VariableValveDevice variableValveDevice, int level, bool ignoreSwing)
        {
            Runner(variableValveDevice, level, ignoreSwing);
        }

        private void Runner(VariableValveDevice variableValveDevice, int level, bool ignoreSwing)
        {
            VariableValveDevice = variableValveDevice;
            VariableValveDevice.IsRunning = true;
            _delayMilli = 1000;
            
            if( level != 0)
            {
                Level = (int) Math.Floor((double) level/10)*10;
            }
            else
            {
                Level = 0;
            }

            if (VariableValveDevice.FullSwingSeconds == 0)
                VariableValveDevice.FullSwingSeconds = 90;

            _step = 100/(double) VariableValveDevice.FullSwingSeconds;


            IgnoreSwing = ignoreSwing;
            var thrd = new Thread(DoRun);

            thrd.Start();
        }

        private void UpdateSwing(Object stateInfo)
        {
            if (_count <= VariableValveDevice.FullSwingSeconds)
            {
                VariableValveDevice.Swing += _newvalue;
                ++_count;
            }
        }

        private void DoRun()
        {
            if (Level == VariableValveDevice.Swing && !IgnoreSwing)
            {
                VariableValveDevice.IsRunning = false;
                return;
            }


            int runSecondsTill = VariableValveDevice.FullSwingSeconds;

            if (!IgnoreSwing)
                runSecondsTill = (int) (VariableValveDevice.FullSwingSeconds*(VariableValveDevice.Swing - Level)*.01);

            if (runSecondsTill < 0)
                runSecondsTill = runSecondsTill*-1;


            var intervalTime = new TimeSpan(0, 0, 0, 0, _delayMilli);
            var delaylTime = new TimeSpan(0, 0, 0, 0, 250);
            var stopTime = new TimeSpan(0, 0, 0, 0, -1);
            TimerCallback timerDelegate = UpdateSwing;


            BeerProgram.TurnOnRelay(VariableValveDevice.RelayLoad);

            if (Level > VariableValveDevice.Swing)
            {
                //OPEN
                BeerProgram.TurnOffRelay(VariableValveDevice.RelayControl);
                _si = 1;
            }
            else
            {
                //CLOSE
                BeerProgram.TurnOnRelay(VariableValveDevice.RelayControl);
                _si = -1;
            }
            _newvalue = _step*(_delayMilli/(double) 1000)*_si;

            var tt = new Timer(timerDelegate, null, delaylTime, intervalTime);
            Thread.Sleep(runSecondsTill*1000);
            tt.Change(stopTime, intervalTime);

            BeerProgram.TurnOffRelay(VariableValveDevice.RelayControl);
            BeerProgram.TurnOffRelay(VariableValveDevice.RelayLoad);
            VariableValveDevice.IsRunning = false;

            if (VariableValveDevice.IsThreeWay)
                VariableValveDevice.Swing = Level;
        }
    }
